# -*- coding: utf-8 -*-
"""
    xfree.auth.services.reauthentication
    ~~~~~~~~~~~~~~~~~~~~~
    文件描述：xfree中处理重新认证需求的工具。
    作者：xfree
    邮箱：cy_83k@qq.com
"""

import logging

from flask_babelplus import gettext as _
from werkzeug.security import check_password_hash

# 从相关模块导入各种重新认证相关的类
from...core.auth.authentication import (
    PostReauthenticateHandler,
    ReauthenticateFailureHandler,
    ReauthenticateManager,
    ReauthenticateProvider,
    StopAuthentication,
)
from...utils.helpers import time_utcnow

# 获取日志记录器
logger = logging.getLogger(__name__)


class DefaultxfreeReauthProvider(ReauthenticateProvider):
    """
    xfree中的默认重新认证提供器，它将提供的密码与当前用户的哈希密码进行比较。
    """

    def reauthenticate(self, user, secret):
        """
        执行重新认证操作。

        :param user: 当前用户对象
        :param secret: 用户提供的密码
        :return: 如果密码匹配则返回True
        """
        if check_password_hash(user.password, secret):  # pragma: no branch
            return True


class ClearFailedLoginsOnReauth(PostReauthenticateHandler):
    """
    在成功重新认证后清除失败登录尝试记录的处理程序。
    """

    def handle_post_reauth(self, user):
        """
        处理重新认证后的操作。

        :param user: 当前用户对象
        """
        user.login_attempts = 0


class MarkFailedReauth(ReauthenticateFailureHandler):
    """
    将重新认证失败尝试标记为失败登录及其发生时间的失败处理程序。
    """

    def handle_reauth_failure(self, user):
        """
        处理重新认证失败操作。

        :param user: 当前用户对象
        """
        user.login_attempts += 1
        user.last_failed_login = time_utcnow()


class PluginReauthenticationManager(ReauthenticateManager):
    """
    xfree的默认重新认证管理器，它依赖插件钩子来管理重新认证流程。
    """

    def __init__(self, plugin_manager, session):
        """
        初始化方法。

        :param plugin_manager: 插件管理器对象
        :param session: 数据库会话对象
        """
        self.plugin_manager = plugin_manager
        self.session = session

    def reauthenticate(self, user, secret):
        """
        执行重新认证操作。

        :param user: 当前用户对象
        :param secret: 用户提供的密码
        """
        try:
            result = self.plugin_manager.hook.xfree_reauth_attempt(
                user=user, secret=secret
            )
            if not result:
                raise StopAuthentication(_("Wrong password."))
            self.plugin_manager.hook.xfree_post_reauth(user=user)
        except StopAuthentication:
            self.plugin_manager.hook.xfree_reauth_failed(user=user)
            raise
        finally:
            try:
                self.session.commit()
            except Exception:
                logger.exception("Exception while processing login")
                self.session.rollback()
                raise